//-------------------------------------------------------------------
//	Copyright (c) 2010-2012  Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#ifndef _UX_GEOMETRY_H
#define _UX_GEOMETRY_H

#include <Includes.h>

namespace Engine
{
namespace Resources
{
	struct TMesh;
	struct TMeshAnimation;
	struct TMeshMaterial;
	struct TMeshPhysics;



	//
	// Multi Mesh Raw Data
	//

	struct TMultiMesh
	{
		struct TSubMesh
		{
			static_string<32>	sName;
			TVerticesAttribs	aAttribs;
			gl_primitive::type	eMode;
			gl_index::type		eType;
			uint32				uIdxOffset,
								uIdxCount;
			aabb_f				sBBox;
			///
			TSubMesh(): eMode(gl_primitive::_INIT), eType(gl_index::_INIT), uIdxOffset(0), uIdxCount(0) {}
		};

		binary_buffer_t			aVertices,
								aIndices;
		array< TSubMesh >		aSubMeshes;
		uint32_array_t			aSMIndices,	// SubMesh indices
								aLODs;		// numbers of first elem in aSMIndices
		///
		TMultiMesh() {}
		
		void Clear()
		{
			aVertices.Clear();
			aIndices.Clear();
			aSubMeshes.Clear();
			aSMIndices.Clear();
			aLODs.Clear();
		}

		void Swap(TMultiMesh &sMesh)
		{
			aVertices.SwapMem( sMesh.aVertices );
			aIndices.SwapMem( sMesh.aIndices );
			aSubMeshes.SwapMem( sMesh.aSubMeshes );
			aSMIndices.SwapMem( sMesh.aSMIndices );
			aLODs.SwapMem( sMesh.aLODs );
		}
	};


	//
	// Sceletal Multi Mesh Raw Data
	//

	struct TSceletalMesh
	{
		struct TSubMesh
		{
			TVerticesAttribs	aAttribs;
			gl_primitive::type	eMode;
			gl_index::type		eType;
			uint32				uIdxOffset,
								uIdxCount;
			///
			TSubMesh():
				eMode(gl_primitive::_INIT), eType(gl_index::_INIT), uIdxOffset(0),
				uIdxCount(0) {}
		};
		
		struct TWeight
		{
			vec3				vPos;
			float				fBias;
			uint				uJointIndex;
			///
			TWeight(): fBias(0.f), uJointIndex(0) {}
		};


		binary_buffer_t			aVertices,
								aIndices;
		array< TSubMesh >		aSubMeshes;
		array< TWeight >		aWeights;
		uint32_array_t			aSMIndices,	// SubMesh indices
								aLODs;		// numbers of first elem in aSMIndices
		uint					uJointsPerFrame;
		///
		TSceletalMesh(): uJointsPerFrame(0) {}
		
		void Clear()
		{
			aVertices.Clear();
			aIndices.Clear();
			aSubMeshes.Clear();
			aWeights.Clear();
			aSMIndices.Clear();
			aLODs.Clear();
			uJointsPerFrame = 0;
		}

		void Swap(TSceletalMesh &sMesh)
		{
			aVertices.SwapMem( sMesh.aVertices );
			aIndices.SwapMem( sMesh.aIndices );
			aSubMeshes.SwapMem( sMesh.aSubMeshes );
			aWeights.SwapMem( sMesh.aWeights );
			aSMIndices.SwapMem( sMesh.aSMIndices );
			aLODs.SwapMem( sMesh.aLODs );
			_swap( uJointsPerFrame, sMesh.uJointsPerFrame );
		}
	};


	//
	// Mesh
	//

	struct _EXPORT_ TMesh
	{
		enum EMeshType
		{
			UNKNOWN			= 0,
			MULTI_MESH,
			SCELETAL_MESH,
		};
		
		union UMeshes
		{
			TMultiMesh		*pMesh;
			TSceletalMesh	*pScelet;
			void			*pUnknown;
		};

		EMeshType		eType;
		UMeshes			sData;
		///
		TMesh();
		explicit TMesh(const TMesh &s);
		~TMesh();

		TMesh & operator = (const TMesh &right);
		bool Create(EMeshType type);
		void Clear();
		void Swap(TMesh &);
	
		bool Empty()
		{
			return sData.pUnknown == nullptr;
		}
	};
	
//-------------------------------------------------------------------



	//
	// Frame Animation Raw Data
	//

	struct TFrameAnimData
	{
		struct TTrackInfo
		{
			static_string<32>	sName;
			uint				uTrackType,
								uFirstFrame,	// in aTrackFrames
								uCount;			// frames count
			///
			TTrackInfo(): uTrackType(0), uFirstFrame(0), uCount(0) {}
			TTrackInfo(uint type, uint first, uint count): uTrackType(type), uFirstFrame(first), uCount(count) {}
		};

		typedef array< TTrackInfo >		track_array_t;


		track_array_t	aTracks;
		uint32_array_t	aTrackFrames;
		float			fFrameRate;
		///
		TFrameAnimData(): fFrameRate(0.f) {}
		
		void Clear()
		{
			aTracks.Clear();
			aTrackFrames.Clear();
			fFrameRate = 0.f;
		}

		void Swap(TFrameAnimData &sAnim)
		{
			aTracks.SwapMem( sAnim.aTracks );
			aTrackFrames.SwapMem( sAnim.aTrackFrames );
			_swap( fFrameRate, sAnim.fFrameRate );
		}
	};


	//
	// Sceletal Animation Raw Data
	//

	struct TSceletalAnimData
	{
		struct TJoint
		{
			quat		vOrient;
			vec3		vPos;
			///
			TJoint() {}
			TJoint(const quat &q, const vec3 &v): vOrient(q), vPos(v) {}
		};

		struct TJointInfo
		{
			static_string<32>	sName;
			uint				uParent;
			uint				uGroupBits;
			///
			TJointInfo(): uParent(0), uGroupBits(1) {}
			TJointInfo(const char *name, uint parent, uint group): sName(name), uParent(parent), uGroupBits(group) {}
		};

		struct TTrackInfo
		{
			static_string<32>	sName;
			uint				uFirstFrame,	// in aTrackFrames
								uFramesCount;	// track frames count
			uint				uGroupMask;		// mask bits
			///
			TTrackInfo(): uFirstFrame(0), uFramesCount(0), uGroupMask(-1) {}
		};
		
		typedef array< TJoint >			joint_array_t;
		typedef array< TJointInfo >		jointinfo_array_t;
		typedef array< aabb_f >			bbox_array_t;
		typedef array< TTrackInfo >		track_array_t;


		joint_array_t		aJoints;		// count = uFramesCount * uJointPerFrame
		bbox_array_t		aBBoxes;		// count = sum( _aTracks[i].uFramesCount )
		uint32_array_t		aTrackFrames;
		track_array_t		aTracks;
		jointinfo_array_t	aJointInfos;	// count == uJointPerFrame
		float				fFrameRate;
		uint				uFramesCount,
							uJointPerFrame;
		///
		TSceletalAnimData(): fFrameRate(0.f), uFramesCount(0), uJointPerFrame(0) {}

		void Clear()
		{
			aJoints.Clear();
			aBBoxes.Clear();
			aTrackFrames.Clear();
			aTracks.Clear();
			fFrameRate		= 0.f;
			uFramesCount	= 0;
			uJointPerFrame	= 0;
		}

		void Swap(TSceletalAnimData &sAnim)
		{
			aJoints.SwapMem( sAnim.aJoints );
			aBBoxes.SwapMem( sAnim.aBBoxes );
			aTrackFrames.SwapMem( sAnim.aTrackFrames );
			aTracks.SwapMem( sAnim.aTracks );
			aJointInfos.SwapMem( sAnim.aJointInfos );
			_swap( fFrameRate, sAnim.fFrameRate );
			_swap( uFramesCount, sAnim.uFramesCount );
			_swap( uJointPerFrame, sAnim.uJointPerFrame );
		}
	};


	//
	// Object Transform Animation Raw Data
	//

	struct TTransformAnimData
	{
	};


	//
	// Morphing Animation Raw Data
	//

	struct TMorphingAnimData
	{
	};


	//
	// Mesh Animation
	//

	struct _EXPORT_ TMeshAnimation
	{
		enum EAnimType
		{
			UNKNOWN		= 0,
			MORPHING,
			TRANSFORM,
			SCELETAL,
			FRAME,
		};

		union UAnimTypes
		{
			TFrameAnimData		*	pFrameAnim;
			TSceletalAnimData	*	pSceletal;
			TTransformAnimData	*	pTransform;
			TMorphingAnimData	*	pMorphing;
			void				*	pUnknown;
		};

		EAnimType		eType;
		UAnimTypes		sData;
		///
		TMeshAnimation();
		explicit TMeshAnimation(const TMeshAnimation &s);
		~TMeshAnimation();
		
		TMeshAnimation & operator = (const TMeshAnimation &right);
		bool Create(EAnimType type);
		void Clear();
		void Swap(TMeshAnimation &);
	
		bool Empty()
		{
			return sData.pUnknown == nullptr;
		}
	};

//-------------------------------------------------------------------



	
	//
	// Materials List
	//

	struct TMeshMaterialsList
	{
		struct TSubMeshMaterial
		{
			uint32_array_t	aMeshIndices;
			string			sFileName;
		};

		array< TSubMeshMaterial >	aMaterials;
		///
		TMeshMaterialsList() {}

		void Clear()
		{
			aMaterials.Clear();
		}

		void Swap(TMeshMaterialsList &sMtr)
		{
			aMaterials.SwapMem( sMtr.aMaterials );
		}
	};


	//
	// Mesh Material
	//

	struct _EXPORT_ TMeshMaterial
	{
		enum EMaterialType
		{
			UNKNOWN		= 0,
			MTR_LIST,
		};

		union UMaterialType
		{
			TMeshMaterialsList	*pMtrList;
			void				*pUnknown;
		};

		EMaterialType	eType;
		UMaterialType	sData;
		///
		TMeshMaterial();
		explicit TMeshMaterial(const TMeshMaterial &s);
		~TMeshMaterial();

		TMeshMaterial & operator = (const TMeshMaterial &right);
		bool Create(EMaterialType type);
		void Clear();
		void Swap(TMeshMaterial &);

		bool Empty()
		{
			return sData.pUnknown == nullptr;
		}
	};

//-------------------------------------------------------------------


	//
	// Mesh Physics
	//

	struct _EXPORT_ TMeshPhysics
	{
		///
		TMeshPhysics() {}

		void Clear() {}

		void Swap(TMeshPhysics &) {}
	};

//-------------------------------------------------------------------
	

	//
	// Model
	//

	struct _EXPORT_ TModel
	{
		TMesh				sMesh;
		TMeshAnimation		sAnimation;
		TMeshMaterial		sMaterial;
		TMeshPhysics		sPhysics;
		///
		TModel() {}
	};

//-------------------------------------------------------------------



	struct _EXPORT_ Geometry
	{
		static bool GenerateGrid(TMultiMesh &sGeom, uint uSideInPow2, uint uPatchSize,
								 gl_vertex::type eVertexType, gl_index::type eIndexType);

		static bool CreateCube(TMultiMesh &sGeom, float fSide);
		static bool CreateCubeCM(TMultiMesh &sGeom, float fSide);		// cube for cube map
		static bool CreateCube3D(TMultiMesh &sGeom, float fSide);		// cude for 3d texture
		static bool CreateSphere(TMultiMesh &sGeom, float fRadius);
		static bool CreateGeoSphere(TMultiMesh &sGeom, float fRadius, uint uLevel);
		static bool CreateSphereCM(TMultiMesh &sGeom, float fRadius);	// sphere for cube map
		static bool CreateGrid(TMultiMesh &sGeom, uint uCount, float fQuadSize, uint uPatchSize = 3);
		//static bool ChangePrimitiveType(const binary_buffer_t &aIndices, GLenum eIdxType, GLenum eSrcType, e_mesh_flags::type eResType, binary_buffer_t &aRes);
		static bool MergeMeshes(const array<TMultiMesh> &aMeshes, TMultiMesh &sResult);
		static bool MergeAnimations(const array<TSceletalAnimData> &aAnims, TSceletalAnimData &sResult);
	};

//-------------------------------------------------------------------

}	// Resources
}	// Engine

#endif	// _UX_GEOMETRY_H